﻿using System;
using System.Collections.Generic;
using System.IO;
using TasksDataXml.Data;
using TasksDataXml.Exceptions;
using TasksDataXml.Helpers;

namespace TasksDataXml
{
    public class Controller
    {
        private CategoryContainer _container;
        private string _storagePath = string.Empty;

        public CategoryContainer Container
        {
            get { return _container; }
            set { _container = value; }
        }

        public Controller(string dataRootPath)
        {
            _storagePath = dataRootPath;
        }

        public CategoryContainer LoadData(out CommonException ex, string root)
        {
            List<CommonException> excList = new List<CommonException>();
            try
            {
                var path = PathUtility.GetCategoriesPath(root);
                var serializer = new DataSerializer<CategoryContainer>();
                _container = serializer.ReadFile(path, out ex);

                if (_container == null)
                    _container = InitAsEmptyContainer(path, out ex);

                var taskSerializer = new DataSerializer<Task>();
                path = Path.GetDirectoryName(path);
                foreach (var category in _container.CategoryList)
                {
                    category.AttachCallbacks(null);
                    category.LoadTasks(taskSerializer, path, excList);
                }

                if(excList.Count > 0)
                {
                    var aex = new AggregateException(excList);
                    ex = new CommonException("Not all tasks were loaded", aex);
                }

                return _container;
            }
            catch (Exception e)
            {
                ex = new CommonException("Can't load data", e);
            }

            return null;
        }

        public CategoryContainer InitAsEmptyContainer(string path, out CommonException ex)
        {
            CleanupContainer();
            
            var serializer = new DataSerializer<CategoryContainer>();
            var container = new CategoryContainer();
            var category = new Category();
            category.GuidId = Guid.NewGuid();
            category.Name = "Empty";
            container.AddCategory(category);
            serializer.WriteFile(path, container, out ex);

            Container = container;

            return container;
        }

        private void CleanupContainer()
        {
            if (Container != null)
            {
                Container.Dispose();
                Container = null;
            }
        }

        public void SaveData(out CommonException ex)
        {
            try
            {
                var rootCategoryPath = PathUtility.GetCategoriesPath(_storagePath);
                if(!PathUtility.CheckDirectories(rootCategoryPath))
                {
                    ex = new CommonException(string.Format("Can't create folder for path {0}", rootCategoryPath));
                    return;
                }

                var list = _container.CategoryList;
                var taskSerializer = new DataSerializer<Task>();
                var excList = new List<CommonException>();
                var path = Path.GetDirectoryName(rootCategoryPath);
                foreach (var c in list)
                {
                    c.SaveTasks(taskSerializer, path, excList);
                }

                var serializer = new DataSerializer<CategoryContainer>();
                serializer.WriteFile(rootCategoryPath, _container, out ex);

                if (excList.Count > 0)
                {
                    if (excList.Count == 1)
                        ex = excList[0];
                    else
                        ex = new CommonException(excList);
                }
            }
            catch (Exception e)
            {
                ex = new CommonException("Can't save data", e);
            }
        }

        private void SaveTaskFile(Task data, out CommonException ex)
        {
            try
            {
                var catPath = PathUtility.GetCategoriesPath(null);
                var path = data.GetFullPath(catPath);
                var serializer = new DataSerializer<Task>();
                serializer.WriteFile(path, data, out ex);
            }
            catch (Exception e)
            {
                ex = new CommonException("Can't save data", e);
            }
        }

        private void DeleteTaskFile(string path)
        {
            if(File.Exists(path))
                File.Delete(path);
        }

        public Category AddCategory(Guid parenGuid, out CommonException ex)
        {
            try
            {
                Category parentCategory = null;
                if(parenGuid != Guid.Empty)
                {
                    List<Category> cl;
                    parentCategory = _container.FindCategory(_container.CategoryList, parenGuid, out cl);
                }

                var cat = new Category();
                cat.GuidId = Guid.NewGuid();
                cat.Name = "New category";

                var list = parentCategory != null ? parentCategory.CategoryList : _container.CategoryList;
                list.Add(cat);
                cat.AttachCallbacks(parentCategory);

                SaveData(out ex);
                return cat;
            }
            catch (Exception e)
            {
                ex = new CommonException("Can't load data", e);
            }

            return null;
        }


        public void DeleteCategory(Category cat, out CommonException ex)
        {
            ex = null;
            try
            {
                cat.DeleteMe();

                List<Category> cl;
                var c = _container.FindCategory(_container.CategoryList, cat.GuidId, out cl);
                if(c != null && cl != null && cl.Count > 0)
                {
                    c.DeleteAllTasks();
                    cl.Remove(c);
                    SaveData(out ex);
                }
            }
            catch (Exception exc)
            {
                ex = new CommonException("Can't delete category", exc);
            }
        }

        public Task AddTask(Category cat, string s, out CommonException ex)
        {
            ex = null;
            if (cat == null)
                return null;

            var data = new Task();
            data.GuidId = Guid.NewGuid();
            data.Desc = s;
            data.IsCompleted = false;
            data.IssueDate = DateTime.Now;

            cat.AddTask(data);

            SaveData(out ex);
            if(ex == null)
                SaveTaskFile(data, out ex);

            return data;
        }

        internal bool DeleteTask(Category cat, Task data, out CommonException ex)
        {
            ex = null;
            if (cat == null)
                return false;

            var path = data.GetFullPath(PathUtility.GetCategoriesPath(null));
            if (cat.TaskPathList.Contains(data.Filename))
            {
                cat.RemoveItem(data);
                DeleteTaskFile(path);
            }

            SaveData(out ex);
            return true;
        }

        internal Task UpdateTask(Task data, out CommonException ex)
        {
            SaveTaskFile(data, out ex);

            return data;
        }
    }
}
